#ifndef __TERM__HPP__
#define __TERM__HPP__

#include "Globals.hpp"

// Need this to be a literal type in order to get constexpr working properly
// In particular, Term may not have virtual functions
template <int PLACE, template <int, bool> class VAR = Var, bool is_constant=false>
struct Term {
#include "COMMONTERM.hpp"
private:
  const VAR<PLACE, is_constant> _var;

public:
  static const bool constant = is_constant;

  constexpr Term() : _cst(1), _sign(false), _var(VAR<PLACE, is_constant> ()) {}
  constexpr Term(VAR<PLACE, is_constant> const v, int c=1, bool s=false) 
    : _cst(c), _sign(s), _var(v) {}
  constexpr VAR<PLACE, is_constant> var() {return _var;}
};

// Explicit instantiation for is_constant == true
template <int PLACE, template <int, bool> class VAR>
struct Term <PLACE, VAR, true> {
#include "COMMONTERM.hpp"
private:
  const VAR<PLACE, true> _var;

public:
  static const bool constant = true;

  constexpr Term() : _cst(1), _sign(false), _var(VAR<PLACE, true> ()) {}
  constexpr Term(VAR<PLACE, true> const v, int c=1, bool s=false) 
  : _cst(c), _sign(s), _var(v) {}
  constexpr VAR<PLACE, true> var() {return _var;}

  // implicit conversion from int iff is_constant == true statically 
  // (avoiding enable_if issues by using code duplication and include COMMONTERM.hpp)
  constexpr operator int() const {
    return (!_sign) ? _cst : -_cst;
  }

  // implicit conversion from int iff is_constant == true statically 
  // (avoiding enable_if issues by using code duplication and include COMMONTERM.hpp)
  constexpr Term(const int c) : _cst((c<0) ? -c : c), _sign(c<0), _var(OneVar) { }
};

typedef Term<0, Var, true> ConstantTerm;
// typedef Term<Var, false> VariableTerm;
template <int PLACE> using VariableTerm = Term<PLACE, Var, false>;
constexpr ConstantTerm One(OneVar);


template <int PLACE, bool is_constant> 
std::ostream & operator<<(std::ostream &os, const Term<PLACE, Var, is_constant> &t) {
  expr_depth++;
  os << std::string(expr_depth, '\t') << 
    ((t.constant) ? "CONSTANT" : "") 
     << " TERM : " << "S=" << t.sign() << " c=" << t.cst() << " v=" << t.var() 
     << " -> factor=" << t.factor() << std::endl;
  expr_depth--;
  return os;
}

// Mult a VariableTerm by an integer (which resolves to a ConstantTerm): do
// not allow VariableTerm * VariableTerm (verify this)
// VariableTerm * VariableTerm could go through BinaryExpression but is not
// allowed in general
template <int PLACE> constexpr VariableTerm<PLACE> 
operator*(const ConstantTerm &t1, const VariableTerm<PLACE> &t2) {
  return VariableTerm<PLACE>(t2.var(), t1.cst() * t2.cst(), t1.sign() ^ t2.sign());
}
template <int PLACE> constexpr VariableTerm<PLACE> 
operator*(const VariableTerm<PLACE> &t2, const ConstantTerm &t1) {
  return t1 * t2;
}
// Don't define this operator, it is ambiguous with int * int
// constexpr ConstantTerm operator*(const ConstantTerm &t1, const ConstantTerm &t2) {
//   return ConstantTerm(OneVar, t1.cst() * t2.cst(), t1.sign() ^ t2.sign());
// }

#endif //__TERM__HPP__
